package com.adjy.pathbuilders.state;

import java.io.IOException;

import android.content.Context;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;

import com.adjy.pathbuilders.MainActivity;
import com.adjy.pathbuilders.database.GameDatabase;
import com.adjy.pathbuilders.profile.Player;
import com.adjy.pathbuilders.rendering.GameCanvas;
import com.adjy.pathbuilders.rendering.loading.AnimationFactory;
import com.adjy.pathbuilders.screens.About;
import com.adjy.pathbuilders.screens.ClearingScreen;
import com.adjy.pathbuilders.screens.FacebookConnectScreen;
import com.adjy.pathbuilders.screens.FinishedLevelScreen;
import com.adjy.pathbuilders.screens.GameOver;
import com.adjy.pathbuilders.screens.GameScreen;
import com.adjy.pathbuilders.screens.LevelsMenu;
import com.adjy.pathbuilders.screens.MainMenu;
import com.adjy.pathbuilders.screens.OptionMenu;
import com.adjy.pathbuilders.screens.PauseScreen;
import com.adjy.pathbuilders.screens.SplashScreen;
import com.adjy.pathbuilders.screens.Tutorial;
import com.adjy.pathbuilders.social.FacebookConnect;
import com.adjy.pathbuilders.sounds.SoundManager;
import com.adjy.pathbuilders.world.GameWorld;
import com.adjy.pathbuilders.world.Level;
import com.adjy.pathbuilders.world.LevelManager;
import com.facebook.model.GraphUser;

/**
 * Game class holds all properties of the entire game: Menus, GameWorld,
 * Screens, Options, etc
 */
public class Game {

	private GameState gameState;
	private GameWorld gameWorld;
	private AnimationFactory animationFactory;
	private Context context;
	private LevelManager levelManager;

	// Screens
	private GameScreen gameOver;
	private FinishedLevelScreen finished_level_screen;
	private Tutorial tutorial;
	private SplashScreen splash;
	private GameScreen menu; //FIXME Put all these variables into a list.
	private GameScreen omenu;
	private GameScreen about;
	private GameScreen pauseScreen;
	private LevelsMenu levelsMenu;
	private GameScreen clearingScreen;

	private GameScreen currentScreen;
	private GameState gameStateToSwitch;

	// Asynchronous Variables Flags
	private boolean inputActive;
	private boolean tutorialSeen;
	private MainActivity mainActivity;
	private GraphUser facebook_user;
	private FacebookConnectScreen fbConnect;
	private Player player;
	private FacebookConnect facebookConnect;
	
	/**
	 * Initiates the Game
	 * 
	 * @throws IOException
	 *             Throws IOException if initial loading fails
	 */
	public Game(Context c, MainActivity mainActivity) throws IOException {
		this.player = new Player();
		this.context = c;
		this.mainActivity = mainActivity;
		this.animationFactory = new AnimationFactory(c);
		this.levelManager = new LevelManager(c);
		inputActive = false;

		this.splash = new SplashScreen(animationFactory);
		changeState(GameState.SPLASH);
		tutorialSeen = false;
		this.facebookConnect = FacebookConnect.getInstance();
		this.facebookConnect.setGame(this);
	}

	public void changeState(GameState newGameState) {
		gameStateToSwitch = newGameState;
	}

	private void switchState() {
		currentScreen = null; // Cancel Any Events coming to the old screen
		waitAsynchronousEventsToFinish();

		switch(gameStateToSwitch) {
		case MAIN_MENU:
			splash.release();
			if (gameWorld != null) {
				gameWorld.release();
				pauseScreen.release();
				gameOver.release();
				finished_level_screen.release();
				pauseScreen = null;
				gameWorld = null;
				gameOver = null;
				finished_level_screen = null;
				animationFactory.releaseAnimations();
			}
			menu.load();
			// TODO: Add these into menu
			levelsMenu.load();
			omenu.load();
			about.load();
			tutorial.load();
			fbConnect.load();
			currentScreen = menu;
			SoundManager.getInstance().playMusic("opening");
			break;
		case RESUME_GAME:
			currentScreen = gameWorld;
			break;
		case START_GAME:
			GameDatabase.getInstance().setStarted(true);
			menu.release();
			// TODO: Add these into menu
			levelsMenu.release();
			omenu.release();
			about.release();
			fbConnect.release();
			tutorial.release();
			// Load Stuff
			animationFactory.loadAnimations();
			gameWorld.load();
			pauseScreen.load();
			gameOver.load();
			finished_level_screen.load();
			currentScreen = gameWorld;
			gameStateToSwitch = GameState.RESUME_GAME;
			break;

		case FACEBOOK_CONNECT:
			currentScreen = fbConnect;
			break;

		case GAME_OVER:
			currentScreen = gameOver;
			break;
		case OPTIONS_MENU:
			currentScreen = omenu;
			break;
		case FINISHED_LEVEL:
			sendAction(finished_level_screen);
			currentScreen = finished_level_screen;
			finished_level_screen.resetTimer(); // FIXME: Check better way of doing this
			break;
		case ABOUT:
			currentScreen = about;
			break;
		case TUTORIAL:
			currentScreen = tutorial;
			// resets to get always a fresh screen
			tutorial.reset();
			break;
		case SPLASH:
			splash.load();
			currentScreen = splash;
			break;
		case PAUSED:
			currentScreen = pauseScreen;
			break;
		case LEVELS_MENU:
			currentScreen = levelsMenu;
			// resets to get always a fresh screen
			levelsMenu.reset();
			break;
		case CLEARED:
			currentScreen = clearingScreen;
			break;

		case 	RESTART:
			Level level = gameWorld.getLevel();
			gameWorld = new GameWorld(this, level, animationFactory);
			pauseScreen = new PauseScreen(animationFactory, level, gameWorld, this);
			finished_level_screen = new FinishedLevelScreen(this, gameWorld, animationFactory);
			gameOver = new GameOver(animationFactory, this, gameWorld);

			playBackgroundSoundLevel(level);

			changeState(GameState.START_GAME);
			switchState();
			return; // FIXME think of a better way of doing this
		default:
			Log.e("SetGameState", "Changing to state not defined");
			break;
		}

		gameState = gameStateToSwitch;
	}



	private void sendAction(FinishedLevelScreen screen) {
		Level level = this.gameWorld.getLevel();
		String action = "";
		if(this.gameWorld.getMainChar().getPoints() >= level.getMostCoins()) {
			action = ("[" + level.getLevelID() + "] I got the best ranking on PathBuilders!\n");
		} else if(this.gameWorld.getCurrentTime() <= level.getBestTime()) {
			action += ("[" + level.getLevelID() + "] I got the best time on PathBuilders!");
		}
		if(!action.isEmpty()) screen.actionListener(action);
	}

	private void waitAsynchronousEventsToFinish() {
		// For now, only asynchronous event is Input:
		while (inputActive) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * This method is called right after the constructor. This should load any
	 * resources needed to start the game
	 */
	public void load() {
		try {
			levelManager.loadLevels(); // Load Levels from File
			createScreens();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void createScreens() throws IOException {
		about = new About(animationFactory, this);
		this.fbConnect = new FacebookConnectScreen(animationFactory, this);
		tutorial = new Tutorial(animationFactory, this);
		levelsMenu = new LevelsMenu(animationFactory, levelManager.getAllLevels(), this);
		clearingScreen = new ClearingScreen(animationFactory, this);

		// Splash Screen
		this.menu = new MainMenu(animationFactory, this);
		this.omenu = new OptionMenu(animationFactory, this);
		menu = new MainMenu(animationFactory, this);
		System.out.println("here");
		this.fbConnect = new FacebookConnectScreen(animationFactory, this);
		System.out.println("but not here");
	}

	/**
	 * This method is called every step of the game. It should perform all logic
	 * behind the game
	 * 
	 * @param timePassed
	 *            Elapsed Time in Seconds
	 */
	public void step(float timePassed) {

		if (gameState != gameStateToSwitch)
			switchState();

		if (currentScreen != null) {
			currentScreen.step(timePassed);
		} else {
			Log.e("Game.step()", "Current Screen for state " + currentScreen + " is null");
		}		
	}

	/**
	 * This method should render all elements currently in the game
	 * 
	 * @param mcanvas
	 *            Canvas to be drawn on
	 */
	public void render(GameCanvas mcanvas, float timeElapsed) {
		// Render elements in the game
		switch (gameState) {
		case FACEBOOK_CONNECT:
			fbConnect.render(mcanvas, timeElapsed);
			break;

		case MAIN_MENU:
			menu.render(mcanvas, timeElapsed);
			break;

		case OPTIONS_MENU:
			omenu.render(mcanvas, timeElapsed);
			break;

		case RESUME_GAME:
			this.gameWorld.start(mcanvas, timeElapsed);
			break;

		case GAME_OVER:
			gameOver.render(mcanvas, timeElapsed);
			break;

		case FINISHED_LEVEL:
			this.finished_level_screen.render(mcanvas, timeElapsed);
			break;

		case ABOUT:
			about.render(mcanvas, timeElapsed);
			break;

		case TUTORIAL:
			tutorial.render(mcanvas, timeElapsed);
			tutorialSeen = true;
			break;

		case SPLASH:

			this.splash.render(mcanvas, timeElapsed);

			if (!this.splash.loading()){
				SoundManager.getInstance().playMusic("opening");
				changeState(GameState.MAIN_MENU);
			}
			break;

		case LEVELS_MENU:
			levelsMenu.render(mcanvas, timeElapsed);
			break;

		case PAUSED:
			pauseScreen.render(mcanvas, timeElapsed);
			break;

		case CLEARED:
			clearingScreen.render(mcanvas, timeElapsed);
			break;

		default:
			// FIXME it should be logged, not printed on the screen.
			mcanvas.drawText("Invalid Game State", 50, 50, new Paint());
			break;
		}
	}

	/**
	 * This method is called when the game should be paused or it is interrupted
	 */
	public void pause() {
		if (gameState == GameState.RESUME_GAME) {
			changeState(GameState.PAUSED);
		}
		SoundManager.getInstance().pauseMusic();
	}

	/**
	 * Method to receive any input events coming on the screen
	 * 
	 * @param event
	 * 
	 *            Event from the screen
	 */
	public void input(MotionEvent event) {

		if (currentScreen != null) {
			inputActive = true;
			currentScreen.input(event);
			inputActive = false;
		}
	}

	public void startLevel(Level level) {
		if(level == null) return;
		
		//Retired
		//if(!GameDatabase.getInstance().getStarted() && tutorialSeen == false){
		//	changeState(GameState.TUTORIAL);
		//} else {
		gameWorld = new GameWorld(this, level, animationFactory);
		pauseScreen = new PauseScreen(animationFactory, level, gameWorld, this);
		finished_level_screen = new FinishedLevelScreen(this, gameWorld, animationFactory);
		gameOver = new GameOver(animationFactory, this, gameWorld);

		playBackgroundSoundLevel(level);			
		changeState(GameState.START_GAME);
		//}
	}

	public void restartLevel() {
		changeState(GameState.RESTART);
	}

	public void resume() {
		SoundManager.getInstance().resumeMusic();
	}

	public Context getContext() {
		return context;
	}

	public void setFacebookUser(GraphUser user) {
		this.facebook_user = user;
	}

	public GraphUser getFacebookUser() {
		return this.facebook_user;
	}

	public FacebookConnectScreen getFbConnect() {
		return fbConnect;
	}

	public Player getPlayer() {
		return player;
	}

	public GameWorld getGameWorld() {
		return gameWorld;
	}
	
	public MainActivity getMainActivity() {
		return mainActivity;
	}

	private void playBackgroundSoundLevel(Level level){

		String backGroundSound =  "background_sound_"+level.getCategory();


		//I could get string functions, but i was lazy to programer it.

		if(level.getLevelID().equals("Medium - 1") ||
				level.getLevelID().equals("Medium - 2"))
			backGroundSound += "_0";

		else if(level.getLevelID().equals("Medium - 3") ||
				level.getLevelID().equals("Medium - 4"))
			backGroundSound+= "_1";

		else if(level.getLevelID().equals("Medium - 5") ||
				level.getLevelID().equals("Medium - 6"))
			backGroundSound+= "_2";

		else if(level.getLevelID().equals("Hard - 2"))
			backGroundSound += "_final";

		try{
			SoundManager.getInstance().playMusic(backGroundSound);
		}
		catch(Exception e){
			Log.d("Sound doesn't exist", backGroundSound);
		}
	}
}
